/*
 * @Description  :
 * @Version      : 0.1
 * @Company      : V-Think Development Team
 * @Author       : KiraVerSace@yeah.net
 * @Date         : 2022-05-20 13:29:26
 * @LastEditTime : 2023-07-18 14:40:21
 */
#include <LTE_Cat1_GM5.h>

#undef  LOG_ENABLE
#define LOG_ENABLE  1

LTE_CAT1_GM5 lte;


LTE_CAT1_GM5::LTE_CAT1_GM5(/* args */)
{
    memset(&this->property, 0x00, sizeof(this->property));
    strncpy((char *)this->property.modelType, "LTE-Cat1-GM5", sizeof(this->property.modelType));
    this->property.finishInitFlag = false;

    this->workMode = AT_MODE_NET;
    this->atResponse.reserve(128);
}

LTE_CAT1_GM5::~LTE_CAT1_GM5()
{

}


bool LTE_CAT1_GM5::readPropertySequence(void)
{
	if ((this->readVersion() == true) && (this->readIMEI() == true)
			&& (this->readICCID() == true))
    {
        this->readCSQ();
        this->readNetworkType();
        this->readLocalIP();
        this->readSocketLinkStatus();

        this->property.finishInitFlag = true;

        return true;
    }
    else
    {
        return false;
    }
}

uint8_t LTE_CAT1_GM5::checkSum(const uint8_t *pData, uint16_t length)
{
    uint8_t checkSumResult;

    for (uint16_t i=0; i < length; i++)
    {
        checkSumResult = (checkSumResult + pData[i])&0xFF;
    }

    return checkSumResult;
}

uint16_t LTE_CAT1_GM5::hexToStringArray(const char *src, char *dest)
{
    uint16_t srcLength = strlen(src);

	for (uint16_t i=0; i<srcLength; i++)
	{
		sprintf((char *)(dest + i*2), "%02X", *(src + i));
	}
	*(dest + srcLength*2) = '\0';

	return (srcLength*2);
}

void LTE_CAT1_GM5::flushBuffer(void)
{
	while (this->serial->available())
	{
		this->serial->read();
	}
}

bool LTE_CAT1_GM5::handle(const enum ATCommandIndex index, const char *command)
{
    bool result = false;

    this->atResponse = "";
    this->flushBuffer();

    if (this->serial->availableForWrite() > 0)
    {
        logVerbose(">> %s", command);
        if (this->workMode == AT_MODE_CMD)          // 发送普通AT指令带\r\n
        {
            this->serial->printf("%s%s", command, "\r\n");
        }
        else
        {
            this->serial->printf("%s%s%s", CMD_PW, command, "\r\n");
        }


        uint32_t startMillis =  millis();

		while ((this->serial->available() == 0) && ((millis() - startMillis) < atCommandList[index].timeout))
		{
            delay(32);
		}

        if (this->serial->available())
		{
			delay(100);
			this->atResponse = this->serial->readString();

			logVerbose("<< %s", this->atResponse.c_str());

			if (this->atResponse.indexOf(atCommandList[index].expectResponse) == -1)
			{
				logError("[LTE]No matched response!");

				return false;
			}
			else
			{
				return true;
			}
		}
		else
		{
			logError("[LTE]Timeout, No response!");

            result = false;
		}
    }
    else
    {
        result = false;
    }

    return result;
}

bool LTE_CAT1_GM5::handle(const enum ATCommandIndex index)
{
    return this->handle(index, atCommandList[index].command);
}

void LTE_CAT1_GM5::begin(HardwareSerial &serial, int32_t pwrEnablePin)    // [115200/8/1/NONE]
{
	this->serial       = &serial;
	this->pwrEnablePin = pwrEnablePin;

	digitalWrite(this->pwrEnablePin, LOW);
	pinMode(this->pwrEnablePin, OUTPUT);
	digitalWrite(this->pwrEnablePin, LOW);

	this->serial->begin(115200);
}

void LTE_CAT1_GM5::powerOn()
{
    this->powerOff();

    delay(320);

	digitalWrite(this->pwrEnablePin, HIGH);

    this->workMode = AT_MODE_NET;     // 上电以后模块必然处于NET模式之下

    this->flushBuffer();
}


void LTE_CAT1_GM5::powerOff(void)
{
	digitalWrite(this->pwrEnablePin, LOW);
}

bool LTE_CAT1_GM5::save(void)               // 保存配置并重启
{
	if (this->handle(AT_S) == true)
	{
		this->workMode = AT_MODE_NET;	    // 重启以后自动进入透传模式
		return true;
	}
	else
	{
		return false;
	}
}

bool LTE_CAT1_GM5::closeSDP(void)           // 关闭套接字分发协议
{
    return this->handle(AT_SDPEN_OFF);
}

bool LTE_CAT1_GM5::readCSQ(void)
{
	if (handle(AT_CSQ) == true)
	{
		uint16_t positionStart = atResponse.indexOf("+CSQ:");
		uint16_t positionEnd   = atResponse.indexOf(',', positionStart + 1);

		this->property.rssi = (uint8_t)(atResponse.substring(positionStart+6, positionEnd).toInt());
		return true;
	}
	else
	{
	    this->property.rssi = 0;
		return false;
	}
}

bool LTE_CAT1_GM5::readNetworkType(void) // 2 = GPRS/GSM  4 = 4G
{
    char networkType;

	if (this->handle(AT_SYSINFO) == true)
	{
        if (atResponse.indexOf("No") != -1)
        {
            this->property.networkType = 0;
        }
        else if (atResponse.indexOf("GSM") != -1)
        {
            this->property.networkType = 2;
        }
        else if (atResponse.indexOf("LTE") != -1)
        {
            this->property.networkType = 4;
        }
        else
        {
            this->property.networkType = 0;
        }

        if (this->property.networkType == 0)
        {
            return false;
        }
        else
        {
            return true;
        }
	}
	else
	{
	    this->property.networkType = 0;
		return false;
	}
}

bool LTE_CAT1_GM5::readVersion(void)
{
    if(this->handle(AT_VER) == true)
    {
        /* +VER:V1.0.05.000000.0000 */
		uint16_t positionStart  = atResponse.indexOf("+VER:");
		uint16_t positionEnd    = atResponse.indexOf('.', positionStart);		// First  '.'
				 positionEnd    = atResponse.indexOf('.', positionEnd+1);		// Second '.'
				 positionEnd    = atResponse.indexOf('.', positionEnd+1);		// Third '.'

		strncpy((char *)this->property.version, atResponse.substring(positionStart+5, positionEnd).c_str(),
					sizeof(this->property.version));

		return true;
    }
    else
    {
        strncpy((char *)this->property.version, "None", sizeof(this->property.version));
        return false;
    }
}

bool LTE_CAT1_GM5::readIMEI(void)
{
    if(this->handle(AT_IMEI) == true)
    {
        uint16_t positionStart = atResponse.indexOf("+IMEI:");
		uint16_t positionEnd   = atResponse.indexOf("OK", positionStart + 1) - 4;

		strncpy((char *)this->property.imei, atResponse.substring(positionStart+6, positionEnd).c_str(),
					sizeof(this->property.imei));

		return true;
    }
    else
    {
        strncpy((char *)this->property.imei, "None", sizeof(this->property.imei));
        return false;
    }
}

bool LTE_CAT1_GM5::readICCID(void)
{
	if (this->handle(AT_ICCID) == true)
	{
		uint16_t positionStart = atResponse.indexOf("+ICCID:");
		uint16_t positionEnd   = atResponse.indexOf("OK", positionStart + 1) - 4;

		strncpy((char *)this->property.iccid, atResponse.substring(positionStart+7, positionEnd).c_str(),
					sizeof(this->property.iccid));

		return true;
	}
	else
	{
	    strncpy((char *)this->property.iccid, "None", sizeof(this->property.iccid));
		return false;
	}
}

bool LTE_CAT1_GM5::readLocalIP(void)
{
	if (this->handle(AT_CIP) == true)
	{
		uint16_t positionStart = atResponse.indexOf("+CIP:");
		uint16_t positionEnd   = atResponse.indexOf("OK", positionStart + 1) - 4;

		strncpy((char *)this->property.localIP, atResponse.substring(positionStart+5, positionEnd).c_str(),
					sizeof(this->property.localIP));

        return true;
	}
	else
	{
	    strncpy((char *)this->property.localIP, "None", sizeof(this->property.localIP));
		return false;
	}
}

bool LTE_CAT1_GM5::setSocket(const char *protocol,
                             const char *ip,
                             const uint16_t portNum)
{
    char linkParameter[256];
    memset(linkParameter, '\0', sizeof(linkParameter));

    snprintf(linkParameter, sizeof(linkParameter), "AT+SOCKA=%s,%s,%d",
                protocol, ip, portNum);

    return this->handle(AT_SOCK_SET, linkParameter);
}

bool LTE_CAT1_GM5::openSocket(void)
{
    char command[16];

    snprintf(command, sizeof(command), "AT+SOCKAEN=ON");

    return this->handle(AT_SOCK_ON, command);
}

bool LTE_CAT1_GM5::closeSocket(void)
{
    char command[16];

    memset(command, 0x00, sizeof(command));
    snprintf(command, sizeof(command), "AT+SOCKBEN=OFF");
    this->handle(AT_SOCK_OFF, command);

    memset(command, 0x00, sizeof(command));
    snprintf(command, sizeof(command), "AT+SOCKCEN=OFF");
    this->handle(AT_SOCK_OFF, command);

    memset(command, 0x00, sizeof(command));
    snprintf(command, sizeof(command), "AT+SOCKDEN=OFF");
    return this->handle(AT_SOCK_OFF, command);
}

bool LTE_CAT1_GM5::readSocketLinkStatus(void)
{
    char command[16];

    snprintf(command, sizeof(command), "AT+SOCKALK?");

    if (handle(AT_SOCKLK, command) == false)
    {
        this->property.linkStatus = false;
        return false;
    }
    else
    {
        this->property.linkStatus = true;
        return true;
    }
}

bool LTE_CAT1_GM5::write(const uint8_t *data, uint16_t len)
{
    this->flushBuffer();
    if (this->workMode == AT_MODE_NET)
    {
        if (this->serial->availableForWrite() > 0)
        {
            this->serial->write(data, len);

            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        logError("[LTE]Not in the NET mode!");

        return false;
    }
}

uint16_t LTE_CAT1_GM5::read (uint8_t *data, const uint16_t len, uint32_t timeout)
{
    uint32_t startMillis =  millis();

    while ((this->serial->available() <= len) && ((millis() - startMillis) < timeout))
    {
        delay(10);
    }

    if (this->serial->available())
    {
        return this->serial->readBytes(data, len);
    }
    else
    {
        logError("Timeout, no response form server!");

        return 0;
    }
}
